#include "jpg.h"


JPG::JPG(const string filename,LCD &lcd,int x)
{
    this->jpgname = filename;
    this->x = x;
    fstream jpgfile;
    jpgfile.open(filename.c_str());
    if(!jpgfile.is_open())
    {
        cerr << "打开文件失败" << endl;
        exit(0);
    }
    struct stat jpgstat;
    stat(filename.c_str(),&jpgstat);
    this->size = jpgstat.st_size;
    this->jpgdata = new char[size];
    jpgfile.read(jpgdata,size);
    jpgfile.close();

    jpg2rgb();
    keep(lcd);
    management(lcd);
}
JPG::~JPG()
{
    delete [] jpgdata;
    delete [] jpgmen;
    delete [] jpgplay;
    // 释放为每个char*分配的内存  
    // for(int i = 0; i < x; ++i) {  
    //     for (int j = 0; j < x; ++j) {  
    //         delete[] pz[i][j]; // 释放内存  
    //         pz[i][j] = NULL; // 将指针设置为NULL以避免悬挂指针  
    //     }  
    // } 
    cout << "jpg的析构函数成功调用" << endl;
}
void JPG::jpg2rgb()
{
    // 1，声明解码结构体，以及错误管理结构体
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;

    // 2，使用缺省的出错处理来初始化解码结构体  
    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_decompress(&cinfo);

    // 3，配置该cinfo，使其从 jpgdata 中读取jpgsize个字节
    //    这些数据必须是完整的JPEG数据

    jpeg_mem_src(&cinfo,(const unsigned char*)jpgdata,size);
    // 4，读取JPEG文件的头，并判断其格式是否合法
    if(!jpeg_read_header(&cinfo,TRUE))
    {
        fprintf(stderr, "jpeg_read_header failed: "
            "%s\n", strerror(errno));
        return;
    }

    // 5，开始解码
    jpeg_start_decompress(&cinfo);
    

    this->width  = cinfo.output_width;
    this->height = cinfo.output_height;
    this->bpp    = cinfo.output_components*8;

    // 7，根据图片的尺寸大小，分配一块相应的内存rgbdata
    //    用来存放从jpgdata解码出来的图像数据
    unsigned long linesize = cinfo.output_width * cinfo.output_components;
    unsigned long rgbsize  = linesize * cinfo.output_height; 

    this->rgbsize = rgbsize;

    char *rgbdata = (char *)calloc(1, rgbsize);
    this->jpgmen = new char[rgbsize];

    // 8，循环地将图片的每一行读出并解码到rgb_buffer中
    int line = 0;
    while(cinfo.output_scanline < cinfo.output_height)
    {
        unsigned char *buffer_array[1];
        buffer_array[0] = (unsigned char *)(this->jpgmen + cinfo.output_scanline * linesize);
        jpeg_read_scanlines(&cinfo, buffer_array, 1);
    }

    // 9，解码完了，将jpeg相关的资源释放掉
    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);
}

/*将要显示在lcd上的数据存储在jpgplay中*/
void JPG::keep(LCD &lcd)
{
    // //优化显示时的问题
    // //设置可见区
    // struct fb_var_screeninfo vinfo;
    // struct fb_fix_screeninfo finfo;
    // ioctl(lcd.fb,FBIOGET_VSCREENINFO,&vinfo);
    // ioctl(lcd.fb,FBIOGET_FSCREENINFO,&finfo);

    // //将初始可见区移到屏幕下方
    // vinfo.xoffset = 0;
    // vinfo.yoffset = 0 + lcd.high;
    // ioctl(lcd.fb,FBIOPAN_DISPLAY,&vinfo);

    jpgplay = new char[lcd.tosize];
    for(int j=0 ; j<height && j<lcd.high; j++)
    {
        //偏移量       
        int moffset = width*bpp/8*j;
        int loffset = lcd.wide*lcd.bpp/8*j;
        //拷贝像素
        for(int i=0; i<width && i<lcd.wide; i++)
        {
            memcpy(jpgplay + 4*i + loffset+0, jpgmen + 3*i + moffset+2, 1);
            memcpy(jpgplay + 4*i + loffset+1, jpgmen + 3*i + moffset+1, 1);
            memcpy(jpgplay + 4*i + loffset+2, jpgmen + 3*i + moffset+0, 1);
        }
    }
    // //图片加载完成后将可见区移回到屏幕
    // vinfo.xoffset = 0;
    // vinfo.yoffset = 0;
    // ioctl(lcd.fb,FBIOPAN_DISPLAY,&vinfo);
}

/*用jpgplay显示图片*/
void JPG::display(LCD &lcd)
{
    showinfo();
    memcpy(lcd.men,jpgplay,lcd.tosize);
}

/*给二维数组分配目标*/
void JPG::management(LCD &lcd)
{
    int nwidth = width/x;
    int nheight = height/x;
    int p = nwidth * lcd.bpp/8;
    int q = width * lcd.bpp/8;

    // 设置二维数组的大小为x*x  
    pz.resize(x);  
    for (int i = 0; i < x; ++i) {  
        pz[i].resize(x);  
        for (int j = 0; j < x; ++j) {  
            pz[i][j] = NULL; 
        }  
    }

    // 设置int二维数组的大小为x*x
    /*并将1-x*x放入*/
    int m = 0;
    win.resize(x);  
    for (int i = 0; i < x; ++i) {  
        win[i].resize(x);  
        for (int j = 0; j < x; ++j) {  
            win[i][j] = ++m; 
        }
    }

    // 设置int二维数组的大小为x*x
    /*并将1-x*x放入*/
    m = 0;
    win2.resize(x);  
    for (int i = 0; i < x; ++i) {  
        win2[i].resize(x);  
        for (int j = 0; j < x; ++j) {  
            win2[i][j] = ++m; 
        }  
    }

    for(int n=0;n<x;n++)
    {
        for(int m=0;m<x;m++)
        {
            pz[n][m] = jpgplay + p*m + q*nheight*n;
        }
    }

}

/*将每个碎片显示到lcd上*/
void JPG::playman(LCD &lcd)
{
    int nwidth = width/x;
    int nheight = height/x;
    for(int i=0;i<nheight;i++)
    {
        int moffset = lcd.wide * lcd.bpp/8 * i;
        int loffset = lcd.wide * lcd.bpp/8 * i;
        for(int j=0;j<nwidth;j++)
        {
            for(int m=0;m<x;m++)
            {
                for(int n=0;n<x;n++)
                {
                    memcpy(lcd.pm[m][n] + 4*j + moffset, pz[m][n] + 4*j + loffset, 4);
                }
            }
        }
    }
}

/*打乱二维数组*/
void JPG::randomize()
{
    // 设置随机种子（使用当前时间作为种子）  
    std::srand(static_cast<unsigned int>(std::time(0)));
    int p = 0;
    vector<char *>p1(x*x); //打乱图片
    vector<int>p2(x*x); //用来同步打乱win
    for(int i=0;i<x;i++)
    {
        for(int j=0;j<x;j++)
        {
            p2[p]   = win[i][j];
            p1[p++] = pz[i][j];
        }
    }
    /*需要确保两个二维数组打乱完全相同*/
    for(int i=0;i<20;i++)
    {
        int q = rand()%(x*x-2);
        char *s = p1[q];
        int s1 = p2[q];
        p1[q] = p1[x*x-1];
        p2[q] = p2[x*x-1];
        p1[x*x-1] = s;
        p2[x*x-1] = s1;
        s = NULL;
    }
    p = 0;
    for(int i=0;i<x;i++)
    {
        for(int j=0;j<x;j++)
        {
            win[i][j] = p2[p];
            pz[i][j] = p1[p++];
        }
    }
    for(int i=0;i<x;i++)
    {
        for(int j=0;j<x;j++)
        {
            cout << win[i][j] << " ";
        }
        cout << endl;
    }
    cout << endl;
}

/*通过区块编号得到二维数组下标*/
void JPG::move2(int a,int &b,int &c)
{
    for(int i=0;i<x;i++)
    {
        if( a>(i*x) && a<=((i+1)*x) )
        {
            b = i;
            c = (a - 1) - i*x;
            return;
        }
    }
}

/*图片碎片交换移动*/
void JPG::move(int a,int b,LCD &lcd)
{
    int e,f,g,h;
    move2(a,e,f);
    cout << "e:" << e << "f:" << f << endl;
    move2(b,g,h);
    cout << "g:" << g << "h:" << h << endl;
    /*交换两块图片并同时更新win*/
    char *s = NULL;
    int s1 = win[e][f];
    s = pz[e][f];
    win[e][f] = win[g][h];
    pz[e][f] = pz[g][h];
    win[g][h] = s1;
    pz[g][h] = s;

    int nwidth = width/x;
    int nheight = height/x;
    for(int i=0;i<nheight;i++)
    {
        int moffset = lcd.wide * lcd.bpp/8 * i;
        int loffset = width * lcd.bpp/8 * i;
        for(int j=0;j<nwidth;j++)
        {
            memcpy(lcd.pm[e][f] + 4*j + moffset, pz[e][f] + 4*j + loffset, 4);
            memcpy(lcd.pm[g][h] + 4*j + moffset, pz[g][h] + 4*j + loffset, 4);
        }
    }
    /*方便拼图*/
    for(int i=0;i<x;i++)
    {
        for(int j=0;j<x;j++)
        {
            cout << win[i][j] << " ";
        }
        cout << endl;
    }
    cout << endl;
}

/*判断胜利条件*/
bool JPG::ifwin()
{
    int w = 0;
    for(int i=0;i<x;i++)
    {
        for(int j=0;j<x;j++)
        {
            if(win[i][j] == win2[i][j])
                w++;
        }
    }
    if(w == (x*x))
        return true;
    else
        return false;
}